//
//  Document.m
//  <6.1>RaiseMan
//
//  Created by chen on 16/9/7.
//  Copyright © 2016年 tyrad. All rights reserved.
//

#import "Document.h"
#import "Person.h"
#import "PreferenceController.h" 

@interface Document ()



@end

@implementation Document

/**区分通知是谁发送的*/
static void *RMDocumentKVOContext ;



- (void)windowControllerDidLoadNib:(NSWindowController *)windowController
{
    [super windowControllerDidLoadNib:windowController];
    
    [self.tableView setBackgroundColor:[PreferenceController preferenceTableBgColor]];
}




#pragma mark -- 实现插入后立即编辑的功能  

- (IBAction)createEmployee:(id)sender {
    
    NSWindow *window = [self.tableView window];
    
    //准备结束正在发生的编辑动作
    BOOL editingEnded = [window makeFirstResponder:window];
    
    if (editingEnded == NO) {
        NSLog(@"Unable to end editing");
        return;
    }
    
    NSUndoManager *undo = [self undoManager];
    
    //在这个事件中是否已经发生一个编辑动作?
    if ([undo groupingLevel] > 0) {
        //关闭最后一个组
        [undo endUndoGrouping];
        //打开一个新的组
        [undo beginUndoGrouping];
    }
    
    //创建对象
    Person *p = [self.arrayController newObject];
    //将内容添加到内容队列
    [self.arrayController addObject:p];
    
    //重新排序（如果用户按列来排序）
    [self.arrayController rearrangeObjects];
    
    //获得排序后的队列
    NSArray *arrayed = [self.arrayController arrangedObjects];
    
    //查找刚添加进去的对象
    NSUInteger row = [arrayed indexOfObjectIdenticalTo:p];
    
    
    //开始在第一列上编辑
    [self.tableView editColumn:0 row:row withEvent:nil select:YES];
}


#pragma mark -- 实现单元格的撤销操作  undo存储相反的操作

- (void)insertObject:(Person *)object inEmployeesAtIndex:(NSUInteger)index{
    //将操作的逆序添加到undo栈中
    NSUndoManager *undo = [self undoManager];
    
    [[undo prepareWithInvocationTarget:self] removeObjectFromEmployeesAtIndex:index];
 
    if ([undo isUndoing] == NO) {
        [undo setActionName:@"Adding PersonName"];
    }
    
    [self startObservingPerson:object];
    
    //执行插入操作
    [self.employees insertObject:object atIndex:index];
}


- (void)removeObjectFromEmployeesAtIndex:(NSUInteger)index{
    
    Person *p = [self.employees objectAtIndex:index];
    //将逆序加入undo栈
    NSUndoManager *undo = [self undoManager];
    [[undo prepareWithInvocationTarget:self] insertObject:p inEmployeesAtIndex:index];

    if ([undo isUndoing] == NO) {
        [undo setActionName:@"remove Person"];
    }
    [self stopObservingPerson:p];
    [self.employees removeObjectAtIndex:index];
}



#pragma mark -- KVO 实现文本的撤销操作

- (void)startObservingPerson:(Person *)person {
    
    //context 区分自己的消息以及发送给父类的消息
    
    [person addObserver:self forKeyPath:@"personName"
                options:NSKeyValueObservingOptionOld
                context:&RMDocumentKVOContext];
    
    [person addObserver:self forKeyPath:@"expectedRaise" options:NSKeyValueObservingOptionOld context:&RMDocumentKVOContext];
    
}

- (void)stopObservingPerson:(Person *)person {
    
    @try{
        //如果没有addObserver,执行removeObserver 可能会报异常。
        [person removeObserver:self forKeyPath:@"personName"];
        [person removeObserver:self forKeyPath:@"expectedRaise"];
    }@catch(id anException){
        //...
    }
}


- (void)setEmployees:(NSMutableArray<Person *> *)employees{
    
    for (Person *p in employees) {
        [self stopObservingPerson:p];
    }
    
    _employees = employees ;
    
    for (Person *p in employees) {
        [self startObservingPerson:p];
    }
}


//自定义的方法
- (void)changeKeyPath:(NSString *)keyPath
             ofObject:(id)object
              toValue:(id)newValue {
    //setValue:forKeyPath 将使得kvo方法调用，用于处理undo事宜
    [object setValue:newValue forKeyPath:keyPath];
}


//设置新值时,添加旧值到 undo
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context
{
//    if (context != RMDocumentKVOContext) {
//        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
//        return;
//    }
//    
    
    NSUndoManager *undo = [self undoManager];
    
    id oldValue = [change objectForKey:NSKeyValueChangeOldKey];
    
    if (oldValue == [NSNull null]) {
        oldValue = nil ;
    }
    NSLog(@"old Value = %@",oldValue);
    
    [[undo prepareWithInvocationTarget:self] changeKeyPath:keyPath ofObject:object toValue:oldValue];
}



#pragma mark -- 其他

- (instancetype)init {
    self = [super init];
    if (self) {
        _employees = @[].mutableCopy ;
        
        //接收颜色变化的通知
        [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleColorChanged:) name:BNRBackgroudColorChangedNotification object:nil];
    }
    return self;
}

- (void)handleColorChanged:(NSNotification *)notification
{
    NSLog(@"%@",notification);
    
    //PreferenceController *controller = [notification object];
    NSColor *color = notification.userInfo[@"color"];
    
    self.tableView.backgroundColor = color ;
    
    NSBeep();
    
}

- (void)dealloc{
    [[NSNotificationCenter defaultCenter] removeObserver:self];
}



+ (BOOL)autosavesInPlace {
    return YES;
}

- (NSString *)windowNibName {
    // Override returning the nib file name of the document
    // If you need to use a subclass of NSWindowController or if your document supports multiple NSWindowControllers, you should remove this method and override -makeWindowControllers instead.
    return @"Document";
}

//save / save all / save  as / close 完全不同,但都是处理一个问题:把模型存入文件或者包中 .需要实现下面方法中的一个
//对象把模型作为NSData存储到文件中，NSData本身是一个字符缓冲区,是基于文档程序最流行的方法
/**
 1. 如果不能创建数据对象就返回nil,用户可以看到警告，提示保存失败
 2. 开发者可以传入类型,允许用户将之保存为某格式
 */
- (NSData *)dataOfType:(NSString *)typeName/*用户请求存储的格式*/ error:(NSError **)outError {
    
    //结束编辑状态
    [[self.tableView window] endEditingFor:nil];
    
    //创建NSData对象 
    return [NSKeyedArchiver archivedDataWithRootObject:_employees];
}


//- (NSFileWrapper *)fileWrapperOfType:(NSString *)typeName error:(NSError * _Nullable __autoreleasing *)outError {
//    
//}

//- (BOOL)writeToURL:(NSURL *)url ofType:(NSString *)typeName error:(NSError * _Nullable __autoreleasing *)outError{}





//文档打开 open/open recent/revert to saved 都是从文件或者文件包中获取模型。为了处理这些模型，需要实现下列方法中的一个

- (BOOL)readFromData:(NSData *)data ofType:(NSString *)typeName error:(NSError **)outError {
    
    NSMutableArray *newArray = nil ;
    
    @try {
        
        newArray = [NSKeyedUnarchiver unarchiveObjectWithData:data];
        
    } @catch (NSException *exception) {
        
        NSLog(@"exception = %@", exception);
        
        if (outError) {
            
            NSDictionary *d = @{ NSLocalizedFailureReasonErrorKey :@"文件打开失败" };
            
            *outError = [NSError errorWithDomain:NSOSStatusErrorDomain code:unimpErr userInfo:d];
            
        }
        return NO ;
        
    }
    
    [self setEmployees:newArray];
    
    return YES;
}


//- (BOOL)readFromFileWrapper:(NSFileWrapper *)fileWrapper ofType:(NSString *)typeName error:(NSError * _Nullable __autoreleasing *)outError{\
}

//-(BOOL)readFromURL:(NSURL *)url ofType:(NSString *)typeName error:(NSError * _Nullable __autoreleasing *)outError{\
}


//更新用户界面

//- (void)windowControllerDidLoadNib:(NSWindowController *)windowController{\
    }





@end










